package es.gestorusuarios.bd;

import com.jmcejudo.crypto.PasswordHasher;
import com.jmcejudo.crypto.PasswordHasher.PasswordHash;
import es.gestorusuarios.correo.GeneradorLink;
import java.net.Inet4Address;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 * 
 */
/**
 *
 * @author Alex
 */
public class GestorBD {

    private static final String DRIVER_MYSQL = "com.mysql.jdbc.Driver";
    private static final String JDBC =
            "jdbc:mysql://155.210.68.160:3306/gestion_usuarios";
    private static final String LOGIN_BD = "admin";
    private static final String PASSWORD_BD = "admin";
    private static final int TRUE = 1;
    private java.sql.Connection conexionBD;

    /*
     * 
     */
    public GestorBD() {
        try {
            Class.forName(DRIVER_MYSQL);
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
    }

    /*
     * 
     */
    private PreparedStatement getPreparedStatement(String sql)
            throws SQLException {
        conexionBD = DriverManager.getConnection(JDBC, LOGIN_BD, PASSWORD_BD);
        conexionBD.setAutoCommit(false);
        return conexionBD.prepareStatement(sql);
    }

    /*
     * 
     */
    public Usuario loguearUsuario(String login, String password) {
        Usuario usuario = null;
        String sql = "SELECT u.administrador,u.activado,u.password ,u.salt "
                + "FROM usuario u "
                + "WHERE u.login = ?";
        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setString(1, login);

            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                if (PasswordHasher.isValid(password, rs.getString(3),
                        rs.getString(4))) {

                    usuario = new Usuario(login, rs.getInt(1) == TRUE,
                            rs.getInt(2) == TRUE);
                }
            }

            rs.close();
            ps.close();
            conexionBD.close();

        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return usuario;
        }
    }

    /*
     * 
     */
    public Usuario getUsuario(String login) {
        Usuario usuario = null;
        String sql = "SELECT * "
                + "FROM usuario u "
                + "WHERE u.login = ?";
        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setString(1, login);

            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                usuario = new Usuario(rs.getString(2), rs.getString(3),
                        rs.getString(4), rs.getString(5),
                        rs.getInt(6) == TRUE, rs.getInt(7) == TRUE,
                        rs.getString(10), rs.getString(8), rs.getDate(9),
                        rs.getString(11));
            }

            rs.close();
            ps.close();
            conexionBD.close();

        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return usuario;
        }
    }

    /*
     * 
     */
    public boolean setUsuario(String login, Usuario usuarioModificado) {
        boolean actualizado = false;
        String sql = "UPDATE usuario SET login = ?, password = ? , "
                + "nombre = ?, apellidos = ?, correo = ?, salt = ? "
                + "WHERE login = ?";
        try {
            Usuario usuario = getUsuario(login);

            if (usuarioModificado.getLogin() != null) {
                usuario.setLogin(usuarioModificado.getLogin());
            }
            if (usuarioModificado.getPassword() != null) {
                PasswordHash password =
                        PasswordHasher.hash(usuarioModificado.getPassword());
                usuario.setPassword(password.getHashString());
                usuario.setSalt(password.getSaltString());
            }
            if (usuarioModificado.getNombre() != null) {
                usuario.setNombre(usuarioModificado.getNombre());
            }
            if (usuarioModificado.getApellidos() != null) {
                usuario.setApellidos(usuarioModificado.getApellidos());
            }
            if (usuarioModificado.getCorreo() != null) {
                usuario.setCorreo(usuarioModificado.getCorreo());
            }

            PreparedStatement ps = getPreparedStatement(sql);
            ps.setString(1, usuario.getLogin());
            ps.setString(2, usuario.getPassword());
            ps.setString(3, usuario.getNombre());
            ps.setString(4, usuario.getApellidos());
            ps.setString(5, usuario.getCorreo());
            ps.setString(6, usuario.getSalt());
            ps.setString(7, login);


            ps.executeUpdate();

            conexionBD.commit();

            ps.close();
            conexionBD.close();

            actualizado = true;
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return actualizado;
        }
    }

    /*
     * 
     */
    public boolean setAdministrador(String login, boolean administrador) {
        boolean actualizado = false;
        String sql = "UPDATE usuario SET administrador = ? WHERE login = ?";
        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setInt(1, (administrador ? 1 : 0));
            ps.setString(2, login);

            ps.executeUpdate();

            conexionBD.commit();

            ps.close();
            conexionBD.close();

            actualizado = true;
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return actualizado;
        }
    }

    /*
     * 
     */
    public boolean setActivado(String login, boolean activado) {
        boolean actualizado = false;
        String sql = "UPDATE usuario SET activado = ? WHERE login = ?";
        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setInt(1, (activado ? 1 : 0));
            ps.setString(2, login);

            ps.executeUpdate();

            conexionBD.commit();

            ps.close();
            conexionBD.close();

            actualizado = true;
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return actualizado;
        }
    }

    /*
     * 
     */
    public boolean addUsuario(Usuario usuario) {
        boolean insertado = false;
        String sql = "INSERT INTO usuario (login, password,nombre, apellidos,"
                + "administrador,activado,ip,fecha_registro,correo,salt) "
                + "VALUES (?,?,?,?,?,?,?,?,?,?)";

        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setString(1, usuario.getLogin());
            PasswordHash password = PasswordHasher.hash(usuario.getPassword());
            ps.setString(2, password.getHashString());
            ps.setString(3, usuario.getNombre());
            ps.setString(4, usuario.getApellidos());
            ps.setInt(5, 0);
            ps.setInt(6, 0);
            ps.setString(7,
                    Inet4Address.getLocalHost().getHostAddress());
            ps.setTimestamp(8,
                    new Timestamp(new Date().getTime()));
            ps.setString(9, usuario.getCorreo());
            ps.setString(10, password.getSaltString());

            ps.executeUpdate();

            conexionBD.commit();

            ps.close();
            conexionBD.close();

            this.createUserBan(usuario);
            
            insertado = true;
        } catch (SQLIntegrityConstraintViolationException ex) {
            System.err.println("Login existe");
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return insertado;
        }

    }

    /*
     * 
     */
    public boolean removeUsuario(String login) {
        boolean eliminado = false;
        String sql = "DELETE FROM usuario WHERE login = ?";

        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setString(1, login);

            ps.executeUpdate();

            //eliminado = true;

            conexionBD.commit();

            ps.close();

            conexionBD.close();

            eliminado = true;
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return eliminado;
        }
    }

    /*
     * Devuelve el login asociado a un determinado correo
     */
    public String existsEmail(String _email) {
        String login = null;

        String sql = "SELECT * "
                + "FROM usuario u "
                + "WHERE u.correo = ?";
        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setString(1, _email);

            ResultSet rs = ps.executeQuery();

            if (rs.next()) {
                login = rs.getString(2);
            }

            rs.close();
            ps.close();
            conexionBD.close();

        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return login;
        }
    }

    /*
     * 
     */
    public List<Usuario> listarUsuariosPendientes() {
        List<Usuario> usuarios = new ArrayList();
        String sql = "SELECT u.login,u.nombre,u.apellidos, u.fecha_registro "
                + "FROM usuario u "
                + "WHERE u.activado = 0";
        try {
            PreparedStatement ps = getPreparedStatement(sql);

            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                usuarios.add(new Usuario(rs.getString(1), rs.getString(2),
                        rs.getString(3), rs.getDate(4)));
            }

            rs.close();
            ps.close();
            conexionBD.close();

        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return usuarios;
        }
    }

    /*
     * 
     */
    public List<Usuario> listarUsuariosActivados() {
        List<Usuario> usuarios = new ArrayList();
        String sql = "SELECT u.login,u.nombre,u.apellidos "
                + "FROM usuario u "
                + "WHERE u.activado = 1 AND u.administrador = 0";
        try {
            PreparedStatement ps = getPreparedStatement(sql);

            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                usuarios.add(new Usuario(rs.getString(1), rs.getString(2),
                        rs.getString(3)));
            }

            rs.close();
            ps.close();
            conexionBD.close();

        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return usuarios;
        }
    }

    /*
     * 
     */
    public boolean darDeBajaUsuario(String login) {
        boolean borrado = false;

        try {
            Usuario usuario = getUsuario(login);
            if (usuario != null) {
                this.removeUserBan(usuario);
                if (removeUsuario(login)) {
                    addUsuarioBorrado(usuario);
                    borrado = true;
                }
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return borrado;
        }
    }

    /*
     * 
     */
    private void addUsuarioBorrado(Usuario usuario) throws SQLException {
        String sql = "INSERT INTO usuario_borrado (login,nombre,apellidos,"
                + "ip,fecha_registro,fecha_borrado,correo) "
                + "VALUES (?,?,?,?,?,?,?)";

        PreparedStatement ps = getPreparedStatement(sql);
        ps.setString(1, usuario.getLogin());
        ps.setString(2, usuario.getNombre());
        ps.setString(3, usuario.getApellidos());
        ps.setString(4, usuario.getIp());
        ps.setDate(5, usuario.getFechaRegistro());
        ps.setTimestamp(6, new Timestamp(new Date().getTime()));
        ps.setString(7, usuario.getCorreo());

        ps.executeUpdate();

        conexionBD.commit();

        ps.close();
        conexionBD.close();
    }

    /*
     * 
     */
    private Integer getIdUsuario(String login) throws SQLException {
        Integer id = null;
        String sql = "SELECT u.idUsuario "
                + "FROM usuario u "
                + "WHERE u.login = ?";

        PreparedStatement ps = conexionBD.prepareStatement(sql);
        ps.setString(1, login);

        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            id = rs.getInt(1);
        }

        rs.close();
        ps.close();

        return id;
    }

    /*
     * 
     */
    public boolean isRecuperarPassword(String login, String url) {
        boolean correcto = false;
        String sql = "SELECT r.url "
                + "FROM recuperar_password r "
                + "WHERE r.idUsuario = ? AND r.url = ? AND r.validez > ?";
        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setInt(1, getIdUsuario(login));
            ps.setString(2, url);
            ps.setLong(3, System.currentTimeMillis());

            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                correcto = true;
            }

            rs.close();
            ps.close();
            conexionBD.close();

        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return correcto;
        }
    }

    /*
     * 
     */
    public boolean addRecuperarPassword(String login, String url, long validez) {
        boolean insertado = false;
        String sql = "INSERT INTO recuperar_password (idUsuario, url, validez) "
                + "VALUES (?,?,?)";

        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setInt(1, getIdUsuario(login));
            ps.setString(2, url);
            ps.setLong(3, validez);

            ps.executeUpdate();

            conexionBD.commit();

            ps.close();
            conexionBD.close();

            insertado = true;
        } catch (SQLIntegrityConstraintViolationException ex) {
            System.err.println("Login existe");
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return insertado;
        }
    }

    /*
     * 
     */
    public boolean removeRecuperarPassword(String login) {
        boolean eliminado = false;
        String sql = "DELETE FROM recuperar_password WHERE idUsuario = ?";

        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setInt(1, getIdUsuario(login));

            ps.executeUpdate();

            eliminado = true;

            conexionBD.commit();

            ps.close();

            conexionBD.close();

            eliminado = true;
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return eliminado;
        }
    }
    
    /*
     * Comprueba si un usuario esta baneado
     */
    public boolean estaBaneado(String _login) {
        boolean baneado = false;
        String sql = "SELECT * "
                + "FROM usuario_baneado "
                + "WHERE idUsuario = ? AND intentos = 5 AND tiempo_ban > ?";
        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setInt(1, getIdUsuario(_login));
            ps.setLong(2, System.currentTimeMillis());

            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                baneado = true;
            }

            rs.close();
            ps.close();
            conexionBD.close();

        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return baneado;
        }
    }
    
    /*
     * Aumenta el numero de intentos de loguin de un usuario
     */
    public boolean incrementarIntentos(String _login) {
        boolean actualizado = false;
        String sql = "UPDATE usuario_baneado SET intentos = intentos + 1, tiempo_ban = ? WHERE idUsuario = ? AND intentos < 5";
        try {
            PreparedStatement ps = getPreparedStatement(sql);
            //ps.setLong(1, System.currentTimeMillis() + 60000); // un minuto para pruebas
            ps.setLong(1, System.currentTimeMillis() + 1800000); //lo baneamos durante media hora
            ps.setInt(2, getIdUsuario(_login));

            ps.executeUpdate();

            conexionBD.commit();

            ps.close();
            conexionBD.close();

            actualizado = true;
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return actualizado;
        }
    }
    
    /*
     * 
     */
    public boolean resetearIntentos(String _login) {
        boolean actualizado = false;
        String sql = "UPDATE usuario_baneado SET intentos = 0, tiempo_ban = ? WHERE idUsuario = ?";
        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setLong(1, 0); 
            ps.setInt(2, getIdUsuario(_login));

            ps.executeUpdate();

            conexionBD.commit();

            ps.close();
            conexionBD.close();

            actualizado = true;
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return actualizado;
        }
    }
    
    /*
     * 
     */
    private boolean createUserBan(Usuario _usuario) {
        boolean creado = false;
        String sql = "INSERT INTO usuario_baneado (idUsuario, intentos) "
                + "VALUES (?,?)";

        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setInt(1, getIdUsuario(_usuario.getLogin()));
            ps.setInt(2, 0);

            ps.executeUpdate();
            conexionBD.commit();
            ps.close();
            conexionBD.close();
            creado = true;
        } catch (SQLIntegrityConstraintViolationException ex) {
            ex.printStackTrace();
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return creado;
        }
    
    }

    /*
     * 
     */
    private boolean removeUserBan(Usuario _usuario) {
        boolean eliminado = false;
        String sql = "DELETE FROM usuario_baneado WHERE idUsuario = ?";

        try {
            PreparedStatement ps = getPreparedStatement(sql);
            ps.setInt(1, getIdUsuario(_usuario.getLogin()));

            ps.executeUpdate();
            conexionBD.commit();
            ps.close();
            conexionBD.close();
            eliminado = true;
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            return eliminado;
        }
    }
    
    /*
     * 
     */
    public static void main(String[] argv) {
        GestorBD gestorBD = new GestorBD();

        gestorBD.incrementarIntentos("toni");
        System.out.println(gestorBD.estaBaneado("toni"));
        gestorBD.resetearIntentos("toni");
        
    }
}
